import { defineBoot } from "#q-app/wrappers";
import type { AxiosRequestConfig, AxiosInstance, AxiosHeaderValue, InternalAxiosRequestConfig, AxiosResponse, AxiosError } from "axios";
import axios from "axios";
import common from "_s/common/localstore";
import { Notify, Loading } from "quasar";
import { Router } from "_s/router/index";

declare module "vue" {
  interface ComponentCustomProperties {
    $axios: AxiosInstance;
    $api: AxiosInstance;
    $req: HttpRquest;
  }
}

const requestMap = new Map();

const addPendingRequest = (config: AxiosRequestConfig) => {
  const requestKey = `${config.method}:${config.url}`;
  if (!requestMap.has(requestKey)) {
    config.cancelToken = new axios.CancelToken(cancel => {
      requestMap.set(requestKey, cancel);
    });
  }
};
// 从 pendingRequests 中移除请求
const removePendingRequest = (config: AxiosRequestConfig) => {
  const requestKey = `${config.method}:${config.url}`;
  if (requestMap.has(requestKey)) {
    const cancelToken = requestMap.get(requestKey);
    cancelToken(requestKey);
    requestMap.delete(requestKey);
  }
};

const baseURL = import.meta.env.VITE_BASE_API;

class HttpRquest {
  // baseUrl = '';
  constructor(/*baseUrl = baseURL*/) {
    // this.baseUrl = baseUrl
  }

  interceptors(instance: AxiosInstance, options: AxiosRequestConfig, contentType: AxiosHeaderValue) {
    // 请求拦截
    instance.interceptors.request.use(
      (config: InternalAxiosRequestConfig) => {
        addPendingRequest(config);
        const token = common.getToken();
        config.withCredentials = true;
        config.headers["Authorization"] = token;
        config.headers["Content-Type"] = contentType;
        return config;
      },
      (error: any) => {
        return Promise.reject(error instanceof Error ? error : new Error(error.message || JSON.stringify(error)));
      }
    );
    // 响应拦截
    instance.interceptors.response.use(
      (response: AxiosResponse) => {
        removePendingRequest(response.config);
        const responseType = response.request.responseType;
        if (responseType === "blob" || responseType === "arraybuffer") return response;
        const { data, status } = response;
        const { code } = data;
        if (code == 401) {
          window.location.href = "/#/login";
          Notify.create({ type: "error", message: "无效用户" });
          return;
        }

        if (status === 200) return data;

        Notify.create({ type: "error", message: "服务器出错请稍后再试" });
      },
      (error: AxiosError) => {
        console.log(error);
        removePendingRequest(error.config as AxiosRequestConfig);
        if (error.status == 401) {
          Notify.create({ type: "error", message: "登录过期" });
          common.setToken("");
          void Router.push("/login");
        }
        if (error.code === "ECONNABORTED" && error.message.indexOf("timeout") !== -1) Notify.create({ type: "error", message: "请求超时请稍后再试" });
        if (Loading.isActive) Loading.hide();
        // window.location.href = "/#/errservice";
        return Promise.reject(error);
      }
    );
  }

  request<T>(options: AxiosRequestConfig, contentType: AxiosHeaderValue = "application/json"): Promise<T> {
    const instance = axios.create({
      baseURL
    });
    options.responseType = options.responseType || "json";
    options.timeout = options.timeout || 15000;
    this.interceptors(instance, options, contentType);
    return instance(options);
  }
}

export const req = new HttpRquest();
// Be careful when using SSR for cross-request state pollution
// due to creating a Singleton instance here;
// If any client changes this (global) instance, it might be a
// good idea to move this instance creation inside of the
// "export default () => {}" function below (which runs individually
// for each client)
const api = axios.create({ baseURL });

export default defineBoot(({ app }) => {
  // for use inside Vue files (Options API) through this.$axios and this.$api

  app.config.globalProperties.$axios = axios;
  // ^ ^ ^ this will allow you to use this.$axios (for Vue Options API form)
  //       so you won't necessarily have to import axios in each vue file

  app.config.globalProperties.$api = api;

  app.config.globalProperties.$req = req;
  // ^ ^ ^ this will allow you to use this.$api (for Vue Options API form)
  //       so you can easily perform requests against your app's API
});

export { api };
